package fregat.base.util;

import java.io.Serializable;

/**
 * Basic telegram providing methods to access data in a byte stream.
 *
 * @author Alexander Mann <br><b><i>ECO_L O G</i> &nbsp Logistiksysteme GmbH</b>
 * @version 1.0
 * @deprecated Use at.ecolog.base.net.Telegram instead
 */
public class Telegram implements Serializable {
  // flag, whether byte order is reversed
  protected boolean swapBytes = true;

  // contains telegram data
  protected byte b[];

  /**
   * Creates a new telegram with reversed byte order
   */
  public Telegram() {}

  /**
   * Creates a new telegram with the specified byte order
   * @param swapBytes True, if reversed byte order
   */
  public Telegram(boolean swapBytes) {
    this.swapBytes = swapBytes;
  }

  /**
   * Initializes the telegram -> creates byte array with specified length
   *
   * @param lengthBytes Length of telegram in bytes
   */
  protected void initTelegram(int lengthBytes) {
    b = new byte[lengthBytes];
  }

  /**
   * Resizes the internal byte array and replaces the telegram data from the
   * given byte array
   *
   * @param b Telegram data
   */
  protected void insertTelegram(byte[] b) {
    insertTelegram(b, b.length);
  }

  /**
   * Resizes the internal byte array to the given length and replaces the
   * telegram data from the given byte array from byte[0] to byte[pintLength]
   *
   * @param b         Telegram data
   * @param lengthBytes Length of data to transfer
   */
  protected void insertTelegram(byte[] b, int lengthBytes) {
    b = new byte[lengthBytes];
    System.arraycopy(b, 0, this.b, 0, Math.min(b.length, lengthBytes));
  }

  /**
   * Sets the character value at the specified byte.
   *
   * @param byteNr Byte number
   * @param value  Character to set
   */
  protected void setChar(int byteNr, char value) {
     setChar(byteNr,value,b);
  }

  /**
   * Sets the character value at the specified byte of the given byte array.
   *
   * @param byteNr Byte number
   * @param value  Character to set
   * @param b Byte array
   */
  public static final void setChar(int byteNr, char value,byte[] b) {
    b[byteNr] = (byte) (value & 0xff);
  }

  /**
   * Returns the character value at the specified byte.
   * @param byteNr Byte number
   * @return Character value
   */
  protected char getChar(int byteNr) {
    return getChar(byteNr,b);
  }

  /**
   * Returns the character value at the specified byte of the given byte array.
   * @param byteNr Byte number
   * @param b Byte array
   * @return Character value
   */
  public static final char getChar(int byteNr,byte[] b) {
    return (char) b[byteNr];
  }

  /**
   * Sets the integer value at the specified byte
   *
   * @param byteNr Byte number
   * @param value  Integer value to set
   */
  protected void setNum(int byteNr, int value) {
    setNum(byteNr,value,b);
  }

  /**
   * Sets the integer value at the specified byte of the given byte array.
   *
   * @param byteNr Byte number
   * @param value  Integer value to set
   * @param b Integer value
   */
  public static final void setNum(int byteNr, int value,byte[] b) {
    b[byteNr] = (byte) (value & 0xff);
  }

  /**
   * Returns the integer value at the specified byte
   *
   * @param byteNr Byte number
   * @return Integer value
   */
  protected int getNum(int byteNr) {
    return getNum(byteNr,b);
  }

  /**
   * Returns the integer value at the specified byte of the given byte array
   * @param byteNr Byte number
   * @param b Byte array
   * @return Integer value
   */
  public static final int getNum(int byteNr,byte[] b) {
    return (int) b[byteNr] & 0xff;
  }

  /**
   * Sets or resets the specified bit
   *
   * @param byteNr Byte number
   * @param bitNr  Bit number
   * @param value  Value to set
   */
  public void setBit(int byteNr, int bitNr, boolean value) {
    if (value) {
      b[byteNr] |= (1 << bitNr);
    } else {
      b[byteNr] &= ~(1 << bitNr);
    }
  }

  /**
   * Returns the value of the specified bit of given byte.
   *
   * @param byteNr Byte number
   * @param bitNr  Bit number
   * @return Bit value
   */
  protected boolean getBit(int byteNr, int bitNr) {
    return getBit(byteNr, bitNr, b);
  }

  /**
   * Returns the value of the specified bit of given byte in given byte array
   *
   * @param byteNr Byte number
   * @param bitNr  Bit number
   * @param b      Byte array
   * @return Bit value
   */
  public static final boolean getBit(int byteNr, int bitNr, byte[] b) {
    int intValue = b[byteNr] & (1 << bitNr);
    return (intValue != 0);
  }

  /**
   * Sets the integer value as a word (2 bytes) starting at the specified byte
   *
   * @param byteNr Byte number
   * @param value  Integer value to set
   */
  protected void setWord(int byteNr, int value) {
    setWord(byteNr,value,b,swapBytes);
  }

  /**
   * Sets the integer value as a word (2 bytes) starting at the specified byte
   * of the given byte array.
   * @param byteNr Byte number
   * @param value  Integer value to set
   * @param b Byte array
   * @param swapBytes True, if reversed byte order
   */
  public static final void setWord(int byteNr,int value,byte[] b,boolean swapBytes) {
    b[byteNr + (swapBytes?1:0)] = (byte) (value / 256);
    b[byteNr + (swapBytes?0:1)] = (byte) (value % 256);
  }

  /**
   * Returns the integer value of a word (2 bytes) starting at the specified byte.
   * @param byteNr Byte number
   * @return Integer value
   */
  protected int getWord(int byteNr) {
    return getWord(byteNr,b,swapBytes);
  }

  /**
   * Returns the integer value of a word (2 bytes) starting at the specified byte
   * of the given byte array.
   * @param byteNr Byte number
   * @param b Byte array
   * @param swapBytes True, if reversed byte order
   * @return Integer value
   */
  public static final int getWord(int byteNr,byte[] b,boolean swapBytes) {
    int intValue = (int) b[byteNr + (swapBytes?1:0)] & 0xff;
    intValue = intValue * 256 + ((int) b[byteNr + (swapBytes?0:1)] & 0xff);
    return intValue;
  }

  /**
   * Inserts the given long value (4 bytes) starting at the specified byte
   *
   * @param byteNr Byte number
   * @param value Long value
   */
  protected void setDWord(int byteNr, long value) {
    setDWord(byteNr,value,b,swapBytes);
  }
  /**
   * Inserts the given long value (4 bytes) starting at the specified byte
   * of the given byte array
   *
   * @param byteNr Byte number
   * @param value Long value
   * @param b Byte array
   * @param swapBytes True, if reversed byte order
   */
  public static final void setDWord(int byteNr, long value,byte[] b,boolean swapBytes) {
    setWord(byteNr + (swapBytes?2:0), (int) (value / 65536),b,swapBytes);
    setWord(byteNr + (swapBytes?0:2), (int) (value % 65536),b,swapBytes);
  }

  /**
   * Returns the long value (4 bytes) starting at the specified byte
   *
   * @param byteNr Byte number
   * @return Long value
   */
  protected long getDWord(int byteNr) {
    return getDWord(byteNr,b,swapBytes);
  }

  /**
   * Returns the long value (4 bytes) starting at the specified byte of the given byte array.
   * @param byteNr Byte number
   * @param b Byte array
   * @param swapBytes True, if reversed byte order
   * @return Long value
   */
  public static final long getDWord(int byteNr,byte[] b,boolean swapBytes) {
    int offset = swapBytes?3:0;
    int increment = swapBytes?-1:1;

    long value = (long) b[byteNr + offset] & 0xff;
    offset += increment;
    value = value * 256 + ((long) b[byteNr + offset] & 0xff);
    offset += increment;
    value = value * 256 + ((long) b[byteNr + offset] & 0xff);
    offset += increment;
    value = value * 256 + ((long) b[byteNr + offset] & 0xff);
    return value;
  }

  /**
   * Inserts the given string into the telegram starting at the specified byte
   *
   * @param byteNr Byte number
   * @param value String value
   */
  protected void setString(int byteNr, String value) {
    setString(byteNr,value,b);
  }

  /**
   * Inserts the given string into the given byte array starting at the
   * specified byte.
   *
   * @param byteNr Byte number
   * @param value String value
   */
  public static final void setString(int byteNr, String value, byte[] b) {
    for (int i = 0; i < value.length(); i++) {
      b[i + byteNr] = (byte) value.charAt(i);
    }
  }

  /**
   * Returns the string representation of the telegram's bytes starting at the
   * specified byte
   *
   * @param byteNr   Byte number
   * @param length Length of string (in characters/bytes)
   * @return String value
   */
  protected String getString(int byteNr, int length) {
    return getString(byteNr,length,b);
  }

  /**
   * Returns the string representation of the telegram's bytes starting at the
   * specified byte of the given byte array
   *
   * @param byteNr   Byte number
   * @param length Length of string (in characters/bytes)
   * @param b Byte array
   * @return String value
   */
  public static final String getString(int byteNr, int length,byte[] b) {
    return new String(b, byteNr, length);
  }

  /**
   * Delivers a string with each byte of the telegram shown as hexadecimal number
   *
   * @return Hexadecimal string representation
   */
  public String dumpHex() {
    return dumpHex(b);
  }

  /**
   * Delivers a string with each byte of the given byte array shown as hexadecimal number
   *
   * @param b
   * @return Hexadecimal string representation
   */
  public static String dumpHex(byte[] b) {
    StringBuffer strBuffer = new StringBuffer();

    for (int i = 0; i < b.length; i++) {
      strBuffer.append(i + ":");
      strBuffer.append(hexchar(i,b));
      strBuffer.append(" ");
    }

    return strBuffer.toString();
  }

  /**
   * Returns a string with the hexadecimal number of the specified byte in the
   * given byte array
   *
   * @param byteNr Byte number
   * @param b      Byte array
   * @return Hexadecimal string representation
   */
  public static String hexchar(int byteNr,byte[] b) {
    return hexchar(b[byteNr]);
  }

  /**
   * Returns a string with the hexadecimal string of the given value
   * @param value
   * @return Hexadecimal string representation
   */
  public static String hexchar(int value) {
    if(value < 0) {
      value += 256; // convert to unsigned int
    }
    if (value <= 15)
      return "0" + Integer.toHexString(value).toUpperCase()+"h";
    else
      return Integer.toHexString(value).toUpperCase()+"h";
  }

  /**
   * Delivers a string with each byte of the telegram shown as binary bit display
   *
   * @return Binary string representation
   */
  public String dumpBin() {
    StringBuffer strBuffer = new StringBuffer();

    for (int i = 0; i < b.length; i++) {
      strBuffer.append(i + ":");
      strBuffer.append(binchar(i,b));
      strBuffer.append(" ");
    }

    return strBuffer.toString();
  }



  /**
   * Returns a string with the binary bit display of the specified byte in the
   * given byte array
   *
   * @param byteNr Byte number
   * @param b      Byte array
   * @return Binary string representation
   */
  public static String binchar(int byteNr, byte[] b) {
    int mask = 0x80;
    String str = "";
    for (int i = 0; i < 8; i++) {
      if ((b[byteNr] & mask) != 0) {
        str = str + "1";
      } else {
        str = str + "0";
      }
      mask = mask / 2;
    }
    return str;
  }

  /**
   * Delivers total(real) length of byte array
   *
   * @return Length in bytes
   */
  public int getLength() {
    return b.length;
  }

  /**
   * Compares the telegrams as byte.
   *
   * @param telegram Telegram to compare to
   * @return True if all bytes are equal
   */
  public boolean equals(Telegram telegram) {
    if (telegram.getLength() == getLength()) {
      int i = 0;
      boolean bEqual = true;
      while (i < getLength() && bEqual) {
        bEqual = (b[i] == telegram.b[i]);
        i++;
      }
      return bEqual;
    }
    return false;
  }

  /**
   * @return Binary telegram data
   */
  public byte[] getBytes() {
    return b;
  }
}
